/*
 * Behavior.h
 *
 *  Created on: Dec 15, 2010
 *      Author: tgalluzzo
 */

#ifndef BEHAVIOR_H
#define BEHAVIOR_H

#include <string>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/date_time.hpp>
#include <boost/shared_ptr.hpp>

class Behavior; // Forward declaration
typedef boost::shared_ptr<Behavior> BehaviorPtr;

class Behavior
{
  public:
    typedef enum
    {
        LEAF = 0,
        SELECTOR = 1,
        SEQUENCE  = 2,
        LOOP  = 3
    } VisualType;

	typedef enum
    {
        NOT_RUN = 0,
        RUNNING = 1,
        PASSED  = 2,
        FAILED  = 3
    } State;

    typedef enum
    {
      WHEN_TRUE  = 0,
      WHEN_FALSE = 1,
      WHEN_NEVER = 2,
    } EndCondition;

    Behavior();
    Behavior(const Behavior& other);
    Behavior(Behavior* other);
    Behavior(boost::function<bool (void)> newFunc);
    Behavior(std::string newName, boost::function<bool (void)> newFunc);

    virtual ~Behavior();

    std::string getName() {return name;}
    void setName(std::string name) {this->name = name;}

    State getState() { return *state; }
    double getExecutionTime() { return *executionTime_sec; }
    bool   isLeaf() { return first==0 && second==0; }

    void interrupt();
    void reset();
    boost::function<bool (void)> logger_decorator(boost::function<bool (void)> fn);

    Behavior& operator&&(const Behavior &other);
    Behavior& operator+(const Behavior &other);
    Behavior& operator||(const Behavior &other);
    Behavior& operator*(const Behavior &other);
    Behavior& operator&(const Behavior &other);
    Behavior& operator|(const Behavior &other);
    Behavior& operator!(void);

    Behavior& operator=(const Behavior& other);

    // Name of this node.
    std::string name;

    BehaviorPtr first;
    BehaviorPtr second;

    boost::function<bool (void)> execute;

    bool loop(int times);
    bool loopUntil(bool endCondition);
    bool loopCondition(int times, EndCondition condition);
    bool announce(std::string announcement);
    void printTree(int level = 0);

  protected:

    bool sequence();
    bool plus();
    bool selector();
    bool parallel();
    bool negate();
    bool orboth();
    bool andboth();
    static bool leafWrapper(Behavior& behavior, boost::function<bool (void)> leafFunc);

    boost::shared_ptr<bool> interrupted;
    boost::shared_ptr<State> state;

    boost::shared_ptr<boost::thread> firstThread;
    boost::shared_ptr<boost::thread> secondThread;
    boost::shared_ptr<boost::condition> exitCondition;
    boost::shared_ptr<boost::condition> runCondition;
    boost::shared_ptr<boost::mutex> mutex;
    boost::shared_ptr<int> childrenRunningCount;
    boost::shared_ptr<double> executionTime_sec;

    void executeChildThread(BehaviorPtr child);
};

#define BEHAVIOR(X) (Behavior(#X, X))

inline Behavior& Announce(const Behavior& child, std::string announcement)
{
  Behavior *newBehavior = new Behavior();
  newBehavior->execute = boost::bind(&Behavior::announce, newBehavior, announcement);
  newBehavior->name = "Announce";
  newBehavior->first.reset(new Behavior(child));
  return *newBehavior;
}

inline Behavior& Loop(const Behavior& child, bool endCondition = true)
{
  Behavior *newBehavior = new Behavior();
  newBehavior->execute = boost::bind(&Behavior::loopUntil, newBehavior, endCondition);
  newBehavior->name = "Loop" + endCondition ? "Until True" : " Until False";
  newBehavior->first.reset(new Behavior(child));
  return *newBehavior;
}

inline Behavior& LoopCondition(const Behavior& child, int times, Behavior::EndCondition condition)
{
  Behavior *newBehavior = new Behavior();
  newBehavior->execute = boost::bind(&Behavior::loopCondition, newBehavior, times, condition);
  if(condition == Behavior::WHEN_TRUE)
  {
      newBehavior->name = "Loop_When_True";
  }
  else if (condition == Behavior::WHEN_FALSE)
  {
      newBehavior->name = "Loop_When_False";
  }
  else
  {
      newBehavior->name = "Loop_When_Never";
  }
  newBehavior->first.reset(new Behavior(child));
  return *newBehavior;
}

inline Behavior& Loop(const Behavior& child, int times)
{
  Behavior *newBehavior = new Behavior();
  newBehavior->execute = boost::bind(&Behavior::loop, newBehavior, times);
  std::ostringstream oss;
  oss << times;
  newBehavior->name = "Loop " + oss.str() + "x";
  newBehavior->first.reset(new Behavior(child));
  return *newBehavior;
}

inline bool exceptionBehaviorExec(std::string errorMsg)
{
  std::cerr << errorMsg << std::endl;
  return false;
}

inline Behavior& ExceptionBehavior(std::string errorMsg)
{
  Behavior *newBehavior = new Behavior();
  newBehavior->execute = boost::bind(exceptionBehaviorExec, errorMsg);
  newBehavior->name = "ExceptionBehavior";
  return *newBehavior;
}

#endif /* BEHAVIOR_H */
